﻿
// mfc_opencvDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "mfc_opencv.h"
#include "mfc_opencvDlg.h"
#include "afxdialogex.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CmfcopencvDlg 对话框



CmfcopencvDlg::CmfcopencvDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_MFC_OPENCV_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CmfcopencvDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CmfcopencvDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_COMMAND(ID_MENU_ITEM_ORIGINAL, &CmfcopencvDlg::OnMenuItemOriginal)
	ON_COMMAND(ID_MENU_ITEM_MEAN_FILTER, &CmfcopencvDlg::OnMenuItemMeanFilter)
	ON_COMMAND(ID_MENU_ITEM_GRAY, &CmfcopencvDlg::OnMenuItemGray)
	ON_COMMAND(ID_MENU_ITEM_CANNY, &CmfcopencvDlg::OnMenuItemCanny)
	ON_COMMAND(ID_MENU_ITEM_OPEN, &CmfcopencvDlg::OnMenuItemOpen)
	ON_COMMAND(ID_MENU_ITEM_EXIT, &CmfcopencvDlg::OnMenuItemExit)
	ON_WM_DESTROY()
	ON_COMMAND(ID_MENU_ITEM_SAVE, &CmfcopencvDlg::OnMenuItemSave)
	ON_COMMAND(ID_MENU_ITEM_CAMERA_OPEN, &CmfcopencvDlg::OnMenuItemCameraOpen)
	ON_COMMAND(ID_MENU_ITEM_OPEN_OPERATION, &CmfcopencvDlg::OnMenuItemOpenOperation)
	ON_COMMAND(ID_MENU_ITEM_CLOSE_OPERATION, &CmfcopencvDlg::OnMenuItemCloseOperation)
	ON_COMMAND(ID_MENU_ITEM_BINARYZATION, &CmfcopencvDlg::OnMenuItemBinaryzation)
END_MESSAGE_MAP()


// CmfcopencvDlg 消息处理程序

BOOL CmfcopencvDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时，框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	mMenu.LoadMenuW(IDR_MENU1);//添加菜单
	SetMenu(&mMenu);

	ShowFullScreen();

	namedWindow("ImageShow", CV_WINDOW_AUTOSIZE);//创建OpenCV窗口
	HWND hWnd = (HWND)cvGetWindowHandle("ImageShow");//嵌套opencv窗口
	HWND hParent = ::GetParent(hWnd);
	::SetParent(hWnd, GetDlgItem(IDC_STATIC_IMG)->m_hWnd);
	::ShowWindow(hParent, SW_HIDE);//隐藏运行程序框，并且把它“画”到MFC上
	
	return TRUE;  // 除非将焦点设置到控件，否则返回 TRUE
}

// 如果向对话框添加最小化按钮，则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序，
//  这将由框架自动完成。

void CmfcopencvDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CmfcopencvDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



BOOL CmfcopencvDlg::PreTranslateMessage(MSG* pMsg)
{
	// TODO: 在此添加专用代码和/或调用基类
	//屏蔽ESC关闭窗体/
	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) return TRUE;
	return CDialogEx::PreTranslateMessage(pMsg);
}


void CmfcopencvDlg::OnMenuItemOriginal()
{
	if (pathName.IsEmpty()) {
		MessageBox(L"先打开图片文件",L"提示");
		return;
	}

	std::string fileNameString(CW2A(pathName.GetString()));       //CString 转 string

	srcImage = imread(fileNameString);//opencv读取图片
	
	// 改变图片大小，来适应图片框的大小
	CRect rect;
	GetDlgItem(IDC_STATIC_IMG)->GetClientRect(&rect);

	CString debugInfo;
	debugInfo.Format(L"(%d,%d,%d,%d)",rect.left,rect.top,rect.right,rect.bottom);
	TRACE(debugInfo);

	cv::Rect dst(rect.left, rect.top, rect.right, rect.bottom);
	cv::resize(srcImage, srcImage, cv::Size(rect.Width(), rect.Height()));

	imshow("ImageShow", srcImage);//opencv显示图片
}


void CmfcopencvDlg::OnMenuItemMeanFilter()
{
	if (pathName.IsEmpty()) {
		MessageBox(L"先打开图片文件", L"提示");
		return;
	}
	//进行均值滤波操作
	blur(srcImage, srcImage, Size(3, 3));// 3*3内核 ，起到降噪作用
	imshow("ImageShow", srcImage);//opencv显示图片
}


void CmfcopencvDlg::OnMenuItemGray()
{
	if (pathName.IsEmpty()) {
		MessageBox(L"先打开图片文件", L"提示");
		return;
	}
	//将原图像转换成灰度图像
	cvtColor(srcImage, srcImage, COLOR_BGR2GRAY);
	imshow("ImageShow", srcImage);//opencv显示图片
}


void CmfcopencvDlg::OnMenuItemCanny()
{
	if (pathName.IsEmpty()) {
		MessageBox(L"先打开图片文件", L"提示");
		return;
	}
	//运行Canny算子
	Canny(srcImage, srcImage, 3, 9, 3);
	// 第三四个参数：是两个阈值，上限和下限，如果一个像素的梯度大于上限，则被认为是边缘像素，如果低于下限则被抛弃，如果介于两者之间，只有当其与高于上限阈值的像素连接时才会被接受。
	// 第五个参数：表示模板的大小，如果是3，则表示3 * 3矩阵的大小。
	imshow("ImageShow", srcImage);//opencv显示图片
}


void CmfcopencvDlg::OnMenuItemOpen()
{
	// TODO: 在此添加命令处理程序代码
	LPCTSTR szFilter = 
		L"Image (*.jpg)|*.png|All Files (*.*)|*.*||" ;

	CFileDialog dlg(TRUE, szFilter);
	if (IDOK == dlg.DoModal()) {
		pathName = dlg.GetPathName();
		OnMenuItemOriginal();
	}
}


void CmfcopencvDlg::OnMenuItemExit()
{
	AfxGetMainWnd()->SendMessage(WM_CLOSE);
	stopCapture = true;
}


void CmfcopencvDlg::OnMenuItemSave()
{
	if (pathName.IsEmpty()) {
		MessageBox(L"先打开图片文件", L"提示");
		return;
	}

	LPCTSTR szFilter =
		L"Image (*.jpg)|*.png|All Files (*.*)|*.*||";
	CFileDialog dlg(FALSE, szFilter);

	if (IDOK == dlg.DoModal()) {
		CString savePathName = dlg.GetPathName();
		std::string savefileNameString(CW2A(savePathName.GetString()));       //CString 转 string
		imwrite(savefileNameString, srcImage);
		MessageBox(L"保存成功", L"提示");
	}
}

void CmfcopencvDlg::OnDestroy()
{
	CDialogEx::OnDestroy();
	// TODO: 在此处添加消息处理程序代码
	cvDestroyWindow("ImageShow");
}


void CmfcopencvDlg::OnMenuItemCameraOpen()
{
	// TODO: 在此添加命令处理程序代码
	face_cascade_name = "D:\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_alt.xml";
	eyes_cascade_name = "D:\\opencv\\build\\etc\\haarcascades\\haarcascade_eye_tree_eyeglasses.xml";
	if (!face_cascade.load(face_cascade_name))
	{
		MessageBox(_T("haarcascade_frontalface_alt.xml Error loading"));
		return;
	};

	if (!eyes_cascade.load(eyes_cascade_name))
	{
		MessageBox(_T(" haarcascade_eye_tree_eyeglasses.xmlError loading"));
		return;
	};

	capture.open(0);//捕获外部摄像头,如果只有一个摄像头，就填0
	Mat frame;
	if (capture.isOpened())
	{
		stopCapture = false;
		while (!stopCapture)//循环以达到视频的效果
		{
			capture >> frame;

			if (!frame.empty())
			{
				detectAndDisplay(frame);//识别的函数
				imshow("ImageShow", frame);
				UpdateData(FALSE);
			}
			else
			{
				//::AfxMessageBox(" --(!) No captured frame -- Break!");
				continue;
				//break;
			}
			waitKey(10);
		}
		capture.release();
	}
}


void CmfcopencvDlg::detectAndDisplay(Mat frame)
{
	std::vector<Rect> faces;
	Mat frame_gray;

	cvtColor(frame, frame_gray, CV_BGR2GRAY);
	equalizeHist(frame_gray, frame_gray);

	//-- 多尺寸检测人脸
	face_cascade.detectMultiScale(frame_gray, faces, 1.1, 2, 0 | CASCADE_SCALE_IMAGE, Size(30, 30));

	for (int i = 0; i < faces.size(); i++)
	{
		Point center(faces[i].x + faces[i].width * 0.5, faces[i].y + faces[i].height * 0.5);
		ellipse(frame, center, Size(faces[i].width * 0.5, faces[i].height * 0.5), 0, 0, 360, Scalar(255, 0, 255), 4, 8, 0);

		Mat faceROI = frame_gray(faces[i]);
		std::vector<Rect> eyes;

		//-- 在每张人脸上检测双眼
		eyes_cascade.detectMultiScale(faceROI, eyes, 1.1, 2, 0 | CASCADE_SCALE_IMAGE, Size(30, 30));

		for (int j = 0; j < eyes.size(); j++)
		{
			Point center(faces[i].x + eyes[j].x + eyes[j].width * 0.5, faces[i].y + eyes[j].y + eyes[j].height * 0.5);
			int radius = cvRound((eyes[j].width + eyes[j].height) * 0.25);
			circle(frame, center, radius, Scalar(255, 0, 0), 4, 8, 0);
		}
	}
}


void CmfcopencvDlg::ShowFullScreen()
{
	// 获取屏幕宽高
	int m_nMaxX = GetSystemMetrics(SM_CXSCREEN); 
	int m_nMaxY = GetSystemMetrics(SM_CYSCREEN);

	CRect rcTemp; 
	rcTemp.BottomRight() = CPoint(m_nMaxX, m_nMaxY); 
	rcTemp.TopLeft() = CPoint(0, 0); 

	// 设置窗口全屏
	MoveWindow(&rcTemp);

	// 设置组件全屏
	GetDlgItem(IDC_STATIC_IMG)->MoveWindow(&rcTemp);
}


void CmfcopencvDlg::OnMenuItemOpenOperation()
{
	//开运算，先腐蚀后膨胀的操作
	/*作用：
		去除噪声，消除小物体
		在纤细点处分离物体
		平滑较大物体的边界的同时并不明显改变其面积
	*/
	//自定义核
	Mat element = getStructuringElement(MORPH_RECT,
		Size(5, 5));
	//开运算
	morphologyEx(srcImage, srcImage, MORPH_OPEN, element);
	imshow("ImageShow", srcImage);//opencv显示图片

	// 来自：https://blog.csdn.net/dieju8330/article/details/83827689
}


void CmfcopencvDlg::OnMenuItemCloseOperation()
{
	//关运算，先膨胀后腐蚀的操作
	/*作用：
		排除小型空洞（指黑色区域）
		平滑物体轮廓
		弥合（连接）窄的间断点，沟壑
		填补轮廓线断裂
	*/
	//自定义核
	Mat element = getStructuringElement(MORPH_RECT,
		Size(5, 5));
	//闭运算
	morphologyEx(srcImage, srcImage, MORPH_CLOSE, element);
	imshow("ImageShow", srcImage);//opencv显示图片

	// 来自：https://blog.csdn.net/dieju8330/article/details/83827689
}


void CmfcopencvDlg::OnMenuItemBinaryzation()
{
	// 二值化
	threshold(srcImage, srcImage, 230, 255, CV_THRESH_BINARY_INV);
	imshow("ImageShow", srcImage);//opencv显示图片
}
